﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Threading.Tasks;
using static Devonline.Core.Helper;

namespace Devonline.Core
{
    public static class CollectionExtensions
    {
        /// <summary>
        /// 集合是否为空或 null
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="source"></param>
        /// <returns></returns>
        public static bool IsNullOrEmpty<T>(this IEnumerable<T> source) => source == null || !source.Any();
        /// <summary>
        /// 集合是否不为空或 null
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="source"></param>
        /// <returns></returns>
        public static bool IsNotNullOrEmpty<T>(this IEnumerable<T> source) => source != null && source.Any();

        /// <summary>
        /// 集合乱序
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="source"></param>
        /// <returns></returns>
        public static IEnumerable<T> ToShuffle<T>(this IEnumerable<T> source)
        {
            var count = source.Count();
            var temps = source.ToList();
            var targets = new List<T>(count);
            var random = new Random();
            while (count != 0)
            {
                var index = random.Next(0, count);
                targets.Add(temps[index]);
                temps.RemoveAt(index);
                count--;
            }

            return targets;
        }

        /// <summary>
        /// Collection ToString extension method, concatenate all collection object to a format string 
        /// </summary>
        /// <typeparam name="T">collection object type</typeparam>
        /// <param name="collection">collection object instance</param>
        /// <param name="format">object format, default is "{0}"</param>
        /// <param name="spliter">object spliter, default is "; "</param>
        /// <returns>concatenate all collection object to a format string</returns>
        public static string ToString<T>(this IEnumerable<T> collection, string spliter = AppSettings.DEFAULT_SPLITER_STRING, string format = AppSettings.DEFAULT_FORMAT_STRING)
        {
            ThrowWhenNull(collection, nameof(collection));

            var result = new List<string>();
            foreach (var value in collection)
            {
                result.Add(string.Format(CultureInfo.CurrentCulture, format, value));
            }

            return string.Join(spliter, result);
        }

        /// <summary>
        /// 从类似于 url 的字符串中截取参数列表到键值对
        /// </summary>
        /// <param name="value"></param>
        /// <param name="outer"></param>
        /// <param name="inner"></param>
        /// <returns></returns>
        public static ICollection<KeyValuePair<string, string>> ToKeyValuePairs(this string value, char outer = AppSettings.CHAR_ADD, char inner = AppSettings.CHAR_EQUAL)
        {
            if (value.IsNotNullOrEmpty())
            {
                var pairs = value.Split(outer, StringSplitOptions.RemoveEmptyEntries);
                if (pairs.IsNotNullOrEmpty())
                {
                    var values = new Dictionary<string, string>();
                    foreach (var pair in pairs)
                    {
                        if (pair.IsNotNullOrEmpty())
                        {
                            var keyValue = pair.Split(inner, StringSplitOptions.RemoveEmptyEntries);
                            var key = keyValue[0];
                            if (key.IsNotNullOrEmpty())
                            {
                                values.Add(key, keyValue[1] ?? string.Empty);
                            }
                        }
                    }

                    return values;
                }
            }

            return null;
        }
        /// <summary>
        /// 将键值对转换为类似于 url 的包含参数的字符串
        /// </summary>
        /// <param name="value"></param>
        /// <param name="outer"></param>
        /// <param name="inner"></param>
        /// <returns></returns>
        public static string ToString(this ICollection<KeyValuePair<string, string>> keyValuePairs, char outer = AppSettings.CHAR_ADD, char inner = AppSettings.CHAR_EQUAL)
        {
            ThrowWhenNull(keyValuePairs, nameof(keyValuePairs));

            if (keyValuePairs.IsNotNullOrEmpty())
            {
                var strings = new List<string>();
                foreach (var keyValue in keyValuePairs)
                {
                    strings.Add(keyValue.Key + inner + (keyValue.Value ?? string.Empty));
                }

                return string.Join(outer, strings);
            }

            return null;
        }
        /// <summary>
        /// 将键值对转换为对象
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="keyValuePairs"></param>
        /// <returns></returns>
        public static T ToObject<T>(this ICollection<KeyValuePair<string, string>> keyValuePairs) where T : class, new()
        {
            ThrowWhenNull(keyValuePairs, nameof(keyValuePairs));

            if (keyValuePairs.IsNotNullOrEmpty())
            {
                var t = new T();
                var type = typeof(T);
                foreach (var keyValue in keyValuePairs)
                {
                    var propertyInfo = type.GetProperty(keyValue.Key.FirstCharToUpper());
                    if (propertyInfo != null)
                    {
                        propertyInfo.SetValue(t, keyValue.Value == null ? null : Convert.ChangeType(keyValue.Value, propertyInfo.PropertyType, CultureInfo.CurrentCulture));
                    }
                }

                return t;
            }

            return default;
        }

        /// <summary>
        /// 字节流转字符串数组
        /// </summary>
        /// <param name="bytes"></param>
        /// <param name="spliter"></param>
        /// <returns></returns>
        public static string ToHexString(this IEnumerable<byte> bytes, char spliter = AppSettings.CHAR_SPACE)
        {
            if (bytes.IsNotNullOrEmpty())
            {
                var value = BitConverter.ToString(bytes.ToArray());
                if (spliter != AppSettings.CHAR_HLINE)
                {
                    value = value.Replace(AppSettings.CHAR_HLINE, spliter);
                }

                return value;
            }

            return null;
        }
        /// <summary>
        /// 字符串数组转字节流
        /// </summary>
        /// <param name="hexString"></param>
        /// <param name="spliter"></param>
        /// <returns></returns>
        public static byte[] ToBinary(this string hexString, char spliter = AppSettings.CHAR_SPACE)
        {
            if (hexString.IsNotNullOrEmpty())
            {
                if (spliter == char.MinValue)
                {
                    var bytes = new byte[hexString.Length / AppSettings.UNIT_TWO];
                    for (int i = 0; i < hexString.Length; i += AppSettings.UNIT_TWO)
                    {
                        bytes[i] = Convert.ToByte(hexString.Substring(i, AppSettings.UNIT_TWO), AppSettings.UNIT_SIXTEEN);
                    }

                    return bytes;
                }
                else
                {
                    var binaryArray = hexString.Split(spliter);
                    if (binaryArray.Length > 0)
                    {
                        var bytes = new byte[binaryArray.Length];
                        for (int i = 0; i < binaryArray.Length; i++)
                        {
                            if (binaryArray[i].IsNotNullOrEmpty())
                            {
                                bytes[i] = Convert.ToByte(binaryArray[i], AppSettings.UNIT_SIXTEEN);
                            }
                        }

                        return bytes;
                    }
                }
            }

            return null;
        }

        /// <summary>
        /// 添加或更新键值对
        /// </summary>
        /// <typeparam name="TKey"></typeparam>
        /// <typeparam name="TValue"></typeparam>
        /// <param name="this"></param>
        /// <param name="key">键</param>
        /// <param name="value">值</param>
        /// <returns></returns>
        public static TValue AddOrUpdate<TKey, TValue>(this IDictionary<TKey, TValue> @this, TKey key, TValue value)
        {
            ThrowWhenNull(@this, nameof(@this));

            if (!@this.ContainsKey(key))
            {
                @this.Add(new KeyValuePair<TKey, TValue>(key, value));
            }
            else
            {
                @this[key] = value;
            }

            return @this[key];
        }
        /// <summary>
        /// 添加或更新键值对
        /// </summary>
        /// <typeparam name="TKey"></typeparam>
        /// <typeparam name="TValue"></typeparam>
        /// <param name="this"></param>
        /// <param name="key">键</param>
        /// <param name="addValue">添加时的值</param>
        /// <param name="updateValueFactory">更新时的操作</param>
        /// <returns></returns>
        public static TValue AddOrUpdate<TKey, TValue>(this IDictionary<TKey, TValue> @this, TKey key, TValue addValue, Func<TKey, TValue, TValue> updateValueFactory)
        {
            ThrowWhenNull(@this, nameof(@this));

            if (!@this.ContainsKey(key))
            {
                @this.Add(new KeyValuePair<TKey, TValue>(key, addValue));
            }
            else
            {
                ThrowWhenNull(updateValueFactory, nameof(updateValueFactory));
                @this[key] = updateValueFactory(key, @this[key]);
            }

            return @this[key];
        }
        /// <summary>
        /// 添加或更新键值对
        /// </summary>
        /// <typeparam name="TKey"></typeparam>
        /// <typeparam name="TValue"></typeparam>
        /// <param name="this"></param>
        /// <param name="key">键</param>
        /// <param name="addValueFactory">添加时的操作</param>
        /// <param name="updateValueFactory">更新时的操作</param>
        /// <returns></returns>
        public static TValue AddOrUpdate<TKey, TValue>(this IDictionary<TKey, TValue> @this, TKey key, Func<TKey, TValue> addValueFactory, Func<TKey, TValue, TValue> updateValueFactory)
        {
            ThrowWhenNull(@this, nameof(@this));

            if (@this.ContainsKey(key))
            {
                ThrowWhenNull(updateValueFactory, nameof(updateValueFactory));
                @this[key] = updateValueFactory(key, @this[key]);
            }
            else
            {
                ThrowWhenNull(addValueFactory, nameof(addValueFactory));
                @this.Add(new KeyValuePair<TKey, TValue>(key, addValueFactory(key)));
            }

            return @this[key];
        }
        /// <summary>
        /// 移除符合条件的元素
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="this"></param>
        /// <param name="where"></param>
        public static void RemoveWhere<T>(this ICollection<T> @this, Func<T, bool> @where)
        {
            ThrowWhenNull(@this, nameof(@this));
            ThrowWhenNull(@where, nameof(@where));

            foreach (var obj in @this.Where(where).ToList())
            {
                @this.Remove(obj);
            }
        }

        /// <summary>
        /// 添加多个元素
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="this"></param>
        /// <param name="values"></param>
        public static void AddRange<T>(this ICollection<T> @this, params T[] values)
        {
            ThrowWhenNull(@this, nameof(@this));
            ThrowWhenNull(values, nameof(values));

            foreach (var obj in values)
            {
                @this.Add(obj);
            }
        }
        /// <summary>
        /// 添加符合条件的多个元素
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="this"></param>
        /// <param name="predicate"></param>
        /// <param name="values"></param>
        public static void AddRange<T>(this ICollection<T> @this, Func<T, bool> predicate, params T[] values)
        {
            ThrowWhenNull(@this, nameof(@this));
            ThrowWhenNull(@this, nameof(predicate));

            foreach (var value in values)
            {
                if (predicate(value))
                {
                    @this.Add(value);
                }
            }
        }
        /// <summary>
        /// 添加不重复的元素
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="this"></param>
        /// <param name="values"></param>
        public static void AddRangeIfNotContains<T>(this ICollection<T> @this, params T[] values)
        {
            ThrowWhenNull(@this, nameof(@this));
            ThrowWhenNull(@this, nameof(values));

            foreach (T obj in values)
            {
                if (!@this.Contains(obj))
                {
                    @this.Add(obj);
                }
            }
        }

        /// <summary>
        /// 原始对象集合转为目标对象集合
        /// </summary>
        /// <typeparam name="TSource"></typeparam>
        /// <typeparam name="TTarget"></typeparam>
        /// <param name="sources"></param>
        /// <param name="convert"></param>
        /// <returns></returns>
        public static IEnumerable<TTarget> ConvertAll<TSource, TTarget>(this IEnumerable<TSource> sources, Func<TSource, TTarget> convert = null)
        {
            ThrowWhenNull(sources, nameof(sources));

            var sourceType = typeof(TSource);
            var targetType = typeof(TTarget);
            if (sourceType == typeof(object) && sources.IsNotNullOrEmpty())
            {
                sourceType = sources.FirstOrDefault().GetType().GetCoreType();
            }

            //输入模型类型和输出模型类型不匹配时, 进行数据转换, 依据当前流程节点配置的 schame 进行转换
            if (sources.IsNullOrEmpty() || sourceType == targetType)
            {
                return sources as IEnumerable<TTarget>;
            }

            // 整体转换不可用, 因为转换不了内部会抛异常, 导致整个数据转换失败
            // Data = Data.ToList().ConvertAll(ConvertModel);
            var targets = new List<TTarget>();
            Parallel.ForEach(sources, source =>
            {
                TTarget target = (convert != null) ? convert(source) : source.CopyTo<TTarget>();
                if (target != null)
                {
                    lock (targets)
                    {
                        targets.Add(target);
                    }
                }
            });

            return targets;
        }

        /// <summary>
        /// 返回一个集合的新的引用, 并据参数清空旧的集合
        /// TODO 有线程问题
        /// </summary>
        /// <typeparam name="T">原始集合泛型类型</typeparam>
        /// <param name="data">原始集合</param>
        /// <param name="count"></param>
        /// <param name="clear">是否需要清空旧的集合</param>
        /// <returns></returns>
        public static ICollection<T> GetFromCollection<T>(this ICollection<T> data, int count = 0, bool clear = true)
        {
            ThrowWhenNull(data, nameof(data));

            lock (data)
            {
                var list = (count <= 0 || count >= data.Count) ? data.ToList() : data.Take(count).ToList();
                if (clear)
                {
                    data.Clear();
                }

                return list;
            }
        }
        /// <summary>
        /// 返回队列的 count 个元素副本，并根据参数决定是否要清空集合
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="queue"></param>
        /// <param name="count"></param>
        /// <param name="clear"></param>
        /// <returns></returns>
        public static ICollection<T> GetFromQueue<T>(this Queue<T> queue, int count = 0, bool clear = true)
        {
            ThrowWhenNull(queue, nameof(queue));

            lock (queue)
            {
                var list = (count <= 0 || count >= queue.Count) ? queue.ToList() : queue.Take(count).ToList();
                if (clear)
                {
                    queue.Clear();
                }

                return list;
            }
        }
        /// <summary>
        /// 将集合中所有元素放入新队列中并返回
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="data"></param>
        /// <returns></returns>
        public static Queue<T> ToQueue<T>(this ICollection<T> data)
        {
            ThrowWhenNull(data, nameof(data));

            var queue = new Queue<T>();
            if (data.Count != 0)
            {
                Parallel.ForEach(data, t =>
                {
                    lock (queue)
                    {
                        queue.Enqueue(t);
                    }
                });
            }

            return queue;
        }
        /// <summary>
        /// 将集合中的数据并行放入队列
        /// </summary>
        /// <returns></returns>
        public static void MoveToQueue<T>(this ICollection<T> data, Queue<T> queue)
        {
            ThrowWhenNull(data, nameof(data));

            if (data.Count != 0)
            {
                var list = data.IsReadOnly ? data : data.GetFromCollection();
                Parallel.ForEach(list, t =>
                {
                    lock (queue)
                    {
                        queue.Enqueue(t);
                    }
                });
            }
        }
        /// <summary>
        /// 从队列中弹出并返回一个值
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="queue"></param>
        /// <returns></returns>
        public static T GetFromQueue<T>(this Queue<T> queue)
        {
            ThrowWhenNull(queue, nameof(queue));

            T t = default;
            if (queue.Count > 0)
            {
                lock (queue)
                {
                    if (queue.Count != 0)
                    {
                        t = queue.Dequeue();
                    }
                }
            }

            return t;
        }
        /// <summary>
        /// 从当前数据处理队列取出数据进行处理
        /// </summary>
        /// <typeparam name="T">数据类型参数</typeparam>
        /// <param name="queue">处理队列</param>
        /// <param name="execute">处理方法</param>
        /// <returns></returns>
        public static async Task ExecuteToQueueAsync<T>(this Queue<T> queue, Func<T, Task> execute)
        {
            ThrowWhenNull(queue, nameof(queue));
            ThrowWhenNull(execute, nameof(execute));

            if (queue.Count != 0)
            {
                var t = queue.GetFromQueue();
                if (t != null)
                {
                    await execute(t).ConfigureAwait(false);
                }
            }
        }
        /// <summary>
        /// 对处理当前数据处理集合中的所有数据并行执行 execute 方法
        /// </summary>
        /// <typeparam name="T">数据类型参数</typeparam>
        /// <param name="data">处理集合</param>
        /// <param name="execute">处理方法</param>
        /// <returns></returns>
        public static async Task ExecuteToDataAsync<T>(this ICollection<T> data, Func<T, Task> execute)
        {
            ThrowWhenNull(data, nameof(data));
            ThrowWhenNull(execute, nameof(execute));

            if (data.IsNotNullOrEmpty())
            {
                var list = data.GetFromCollection();
                if (list.IsNotNullOrEmpty())
                {
                    //Parallel.ForEach(list, async t => await execute(t).ConfigureAwait(false));
                    foreach (var t in list)
                    {
                        await execute(t).ConfigureAwait(false);
                    }
                }
            }
        }
        /// <summary>
        /// 对处理当前数据处理集合中的所有数据执行 execute 方法
        /// </summary>
        /// <typeparam name="T">数据类型参数</typeparam>
        /// <param name="data">处理集合</param>
        /// <param name="execute">处理方法</param>
        /// <returns></returns>
        public static async Task ExecuteToDataAsync<T>(this ICollection<T> data, Func<ICollection<T>, Task> execute)
        {
            ThrowWhenNull(data, nameof(data));
            ThrowWhenNull(execute, nameof(execute));

            if (data.Count != 0)
            {
                var list = data.GetFromCollection();
                if (list.IsNotNullOrEmpty())
                {
                    await execute(list).ConfigureAwait(false);
                }
            }
        }
        /// <summary>
        /// 对处理当前数据处理集合中的所有数据并行执行 execute 方法到终结点
        /// </summary>
        /// <typeparam name="T">数据类型参数</typeparam>
        /// <param name="data">处理集合</param>
        /// <param name="execute">处理方法</param>
        /// <returns></returns>
        public static async Task ExecuteToEndpointAsync<T>(this ICollection<T> data, IEndpoint endpoint, Func<IEndpoint, T, Task> execute)
        {
            ThrowWhenNull(data, nameof(data));
            ThrowWhenNull(execute, nameof(execute));

            if (data.Count != 0)
            {
                var list = data.GetFromCollection();
                if (list.IsNotNullOrEmpty())
                {
                    //Parallel.ForEach(list, async t => await execute(endpoint, t).ConfigureAwait(false));
                    foreach (var t in list)
                    {
                        await execute(endpoint, t).ConfigureAwait(false);
                    }
                }
            }
        }
        /// <summary>
        /// 对处理当前数据处理集合中的所有数据执行 execute 方法到终结点
        /// </summary>
        /// <typeparam name="T">数据类型参数</typeparam>
        /// <param name="data">处理集合</param>
        /// <param name="execute">处理方法</param>
        /// <returns></returns>
        public static async Task ExecuteToEndpointAsync<T>(this ICollection<T> data, IEndpoint endpoint, Func<IEndpoint, ICollection<T>, Task> execute)
        {
            ThrowWhenNull(data, nameof(data));
            ThrowWhenNull(execute, nameof(execute));

            if (data.Count != 0)
            {
                var list = data.GetFromCollection();
                if (list.IsNotNullOrEmpty())
                {
                    await execute(endpoint, list).ConfigureAwait(false);
                }
            }
        }
    }
}
